using System;
using System.Collections;
using System.IO;
using System.Xml;
using Widgetsphere.Core.Logging;
using Widgetsphere.Core.Util;

namespace Widgetsphere.Core.DataAccess
{

	public partial class ConfigurationFile
	{
		#region Class's Log Switch
		private static readonly TraceSwitch mLogSwitch = LogClass.WSCommonDataAccess;
		#endregion

		#region Class Members

		private FileInfo mConnectionFile;
		private string mPassword = string.Empty;
		private bool mDirectConnect = true;
		private string mConfigString = string.Empty;

		public event EventHandler ConfigurationChanged;
		
		private const string CONFIGURATION_NODE = "Configuration";
		private const string ENCRYPTED_INFORMATION_NODE = "EncryptedInformation";
		private const string PASSOWRD_NODE = "Password";
		private const string WEB_SERVICE_LOCATION_NODE = "WebServiceLocation";
		private const string CONNECTION_NODE = "Connection";
		private const string DATABASE_CONNECTION_STRING_NODE = "UseDatabaseConnectionString";
		private const string DIRECT_CONNECT_NODE = "UseDirectConnection";
		private const string DEFAULT_CONFIG_FILE = @"config\company.config.xml";
		private const string APP_CONFIG_SETTING = "ConfigurationFile";
		private const string REG_CONFIG_LOCATION = @"Software\Widgetsphere\Company";
		private const string REG_PATH_ITEM = "SharePath";
		private const string REG_FILE_ITEM = "ConfigurationFile";
		private readonly Hashtable _nameValuePairs = new Hashtable();

		#endregion

		#region Constructor

		public ConfigurationFile(string configFile)
		{
			InitilizeConfigurationFile(configFile);
		}

		public ConfigurationFile()
		{
			var fullFileName = GetConfigFilePath();
			InitilizeConfigurationFile(fullFileName);
			this.Open(mConnectionFile.CreationTimeUtc.ToLongDateString());
		}

		#endregion

		#region Properties

		public FileInfo ConnectionFile
		{
			get { return mConnectionFile; }
		}

		public string ConfigString
		{
			get { return mConfigString; }
			set { mConfigString = value; }
		}


		public bool DirectConnect
		{
			get { return mDirectConnect; }
			set { mDirectConnect = value; }
		}

		public Hashtable NameValuePairs
		{
			get { return _nameValuePairs; }
		}

		#endregion

		#region Methods

		private void InitilizeConfigurationFile(string fullFileName)
		{
			mConnectionFile = new FileInfo(fullFileName);
			if (!mConnectionFile.Exists)
				mConnectionFile.Create().Close();
			var watcher = new FileSystemWatcher(mConnectionFile.DirectoryName, mConnectionFile.Name);
			watcher.Changed += new FileSystemEventHandler(watcher_Changed);
		}

		private static string GetConfigFilePath()
		{
			var fullFileName = System.Configuration.ConfigurationManager.AppSettings[APP_CONFIG_SETTING];
			if (fullFileName == null || fullFileName == string.Empty)
			{
				var path = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_PATH_ITEM);
				if (path == null || path == string.Empty)
				{
					path = RegistryHelper.GetLocalMachineRegistryValue(REG_CONFIG_LOCATION, REG_PATH_ITEM);
					var tmpFileName = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_FILE_ITEM, DEFAULT_CONFIG_FILE);
					if (path != null && path != string.Empty)
						fullFileName = StringHelper.EnsureDirectorySeperatorAtEnd(path) + tmpFileName;
					else
						fullFileName = tmpFileName;
				}
				else
				{
					var tmpFileName = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_FILE_ITEM, DEFAULT_CONFIG_FILE);
					fullFileName = StringHelper.EnsureDirectorySeperatorAtEnd(path) + tmpFileName;
				}
			}
			return fullFileName;
		}

		public void Save(string password)
		{
			mPassword = password;

			var xWriter = new XmlTextWriter(mConnectionFile.FullName, System.Text.Encoding.UTF8);
			xWriter.WriteStartDocument();
			xWriter.WriteStartElement(CONFIGURATION_NODE);
			xWriter.WriteElementString(ENCRYPTED_INFORMATION_NODE, GetPropertiesAsEncryptedText());
			xWriter.WriteElementString(PASSOWRD_NODE, GetPasswordHash());
			xWriter.WriteEndElement();
			xWriter.WriteEndDocument();
			xWriter.Flush();
			xWriter.Close();
		}

		public bool Open(string password)
		{
			try
			{
				mPassword = password;
				var oDoc = new XmlDocument();
				oDoc.Load(mConnectionFile.FullName);
				var encryptedInfo = oDoc.SelectSingleNode("//" + ENCRYPTED_INFORMATION_NODE).InnerText;
				var passwordHash = oDoc.SelectSingleNode("//" + PASSOWRD_NODE).InnerText;
				if (ValidPassword(passwordHash))
				{
					SetupPropertiesFromEncryptedText(encryptedInfo);
					return true;
				}
				else
				{
					return false;
				}
			}
			catch (Exception ex)
			{
				MessageLog.LogError(mLogSwitch, ex.ToString());
				throw;
			}
		}

		private string GetEntropy()
		{
			return "aujsywhdtgweysdjwiwlfnvtsfgzjxiked";
		}

		private string GetPropertiesAsEncryptedText()
		{
			var crytographyHelper = CryptoFactory.Create(CryptographyAlgorithm.Rijndael);
			crytographyHelper.Entropy = GetEntropy();
			var encryptedText = crytographyHelper.Encrypt(GetPropertiesAsXml(), StringEncodingType.Hex);
			return encryptedText;
		}

		private void SetupPropertiesFromEncryptedText(string encryptedText)
		{
			var crytographyHelper = CryptoFactory.Create(CryptographyAlgorithm.Rijndael);
			crytographyHelper.Entropy = GetEntropy();
			var decryptString = crytographyHelper.Decrypt(encryptedText, StringEncodingType.Hex);
			this.SetupProperties(decryptString);
		}


		private void SetupProperties(string propertiesAsXml)
		{
			var oDoc = new XmlDocument();
			oDoc.LoadXml(propertiesAsXml);
			var directConnectNode = oDoc.SelectSingleNode("//" + DIRECT_CONNECT_NODE);
			DirectConnect = bool.Parse(directConnectNode.InnerText);
			if (this.DirectConnect)
			{
				var databaseConnectNode = oDoc.SelectSingleNode("//" + DATABASE_CONNECTION_STRING_NODE);
				ConfigString = databaseConnectNode.InnerText;
			}
			else
			{
				var webServiceNode = oDoc.SelectSingleNode("//" + WEB_SERVICE_LOCATION_NODE);
				ConfigString = webServiceNode.InnerText;
			}

			_nameValuePairs.Clear();
			foreach (XmlNode node in oDoc.DocumentElement.ChildNodes)
			{
				switch (node.Name)
				{
					case DIRECT_CONNECT_NODE:
					case DATABASE_CONNECTION_STRING_NODE:
					case WEB_SERVICE_LOCATION_NODE:
						break;
					default:
						_nameValuePairs.Add(node.Name, node.InnerText);
						break;
				}
			}

		}

		/// <summary>
		/// Add the saving of all properties here
		/// </summary>
		/// <returns></returns>
		private string GetPropertiesAsXml()
		{
			var sr = new StringWriter();
			var xWriter = new XmlTextWriter(sr);
			xWriter.WriteStartElement(CONNECTION_NODE);
			xWriter.WriteElementString(DIRECT_CONNECT_NODE, this.DirectConnect.ToString());
			if (this.DirectConnect)
				xWriter.WriteElementString(DATABASE_CONNECTION_STRING_NODE, ConfigString);
			else
				xWriter.WriteElementString(WEB_SERVICE_LOCATION_NODE, ConfigString);

			//Write al key values
			foreach(string key in this.NameValuePairs.Keys)
			{
				xWriter.WriteElementString(key, (string)this.NameValuePairs[key]);
			}

			xWriter.WriteEndElement();
			xWriter.Flush();
			xWriter.Close();
			return sr.ToString();
		}

		private string GetPasswordHash()
		{
			var defaultHash = new HashHelper(HashHelper.ServiceProviderEnum.SHA256);
			return defaultHash.Encrypt(mPassword);
		}

		private bool ValidPassword(string passwordHash)
		{
			if (mPassword == mConnectionFile.CreationTimeUtc.ToLongDateString() ||
				StringHelper.Match(GetPasswordHash(), passwordHash))
			{
				return true;
			}
			return false;
		}

		#endregion

		#region Event Handlers

		protected void OnConfigurationChanged(object sender, System.EventArgs args)
		{
			if (ConfigurationChanged != null)
				ConfigurationChanged(sender, args);
		}

		private void watcher_Changed(object sender, FileSystemEventArgs e)
		{
			this.Open(mConnectionFile.CreationTimeUtc.ToLongDateString());
			OnConfigurationChanged(this, new System.EventArgs());
		}

		#endregion

	}
}
